﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Expect.Core.Errors;
using Expect.Core.Interfaces;

namespace Expect.Core.Fixtures
{
    /// <summary>
    /// Base class for fixtures relying on strict tabular formats like ColumnFixture or SetupFixture
    /// </summary>
    public abstract class TableFixture : AbstractFixture, ITableFixtureFormat
    {
        /// <summary>
        /// Columns definition
        /// </summary>
        public FixtureRow Definition { get; private set; }

        /// <summary>
        /// interpret the fixture definition (e.g compute arguments and definition)
        /// </summary>
        /// <returns></returns>
        public override void Resolve()
        {
            /*if (Fixture.Rows.Count > 0 && Fixture.Definition == null)
            {
                Fixture.Definition = Fixture[0];
                Fixture.RemoveAt(0); // Remove column definition
            }*/
        }

        /// <summary>
        /// Treat a table cell
        /// </summary>
        /// <param name="cell"></param>
        /// <param name="defName"></param>
        /// <returns></returns>
        protected virtual bool? ResolveCell(FixtureCell cell, string defName)
        {
            if (!string.IsNullOrEmpty(cell.Text))
            {
                return Context.SetFieldOrProperty(this, cell, defName);
            }
            else
            {
                if (Context.HasFieldOrProperty(this, defName)) 
                    return true;

                return null;
            }
        }

        /// <summary>
        /// Execute the fixture from the provided row
        /// </summary>
        /// <returns></returns>
        public override bool? RunRow()
        {
            if (Fixture.Rows.Count > 0 && Definition == null)
            {
                Definition = Fixture.Rows[0];
                Definition.Status.Success = true;
                foreach (var cell in Definition)
                {
                    cell.Status.Success = true;
                }
                this.Resolve();
                return null;
            }

            if (Definition != null && Fixture.Rows.Count > 1 && Row != Definition)
            {
                Row.Status.Success = CheckFormat();
                if (!Row.IsSuccessful) return false;

                //IFixtureImplementation fixtureImpl = Fixture.GetImplementation(Context.Catalog);
                BeforeRun();

                for (int i = 0; i < Definition.Count; i++)
                {
                    FixtureCell cellDef = Definition[i];
                    FixtureCell cell = Row[i];

                    RunCell(Fixture.Implementation, cell, cellDef);

                    Row.Status.Success = Row.Status.Success.Value &&
                                         (!cell.Status.Success.HasValue || cell.Status.Success.Value);
                }

                Row.CalculateResult();

                AfterRun();
            }

            return Row.Status.Success;
        }

        private void RunCell(IFixtureImplementation fixtureImpl, FixtureCell cell, FixtureCell cellDef)
        {
            try
            {
                cell.Status.Success = ResolveCell(cell, cellDef.Text);                    
            }
            catch(MethodNotFoundException)
            {
                cell.Status.AddError(ElementNotFoundError.Method(cellDef.Text,this.GetType().FullName, 0));
                cell.Status.Success = false;
                Row.Status.Success = false;
            }
            catch(Exception ex)
            {
                cell.Status.AddError(new UnexpectedError(ex));
                cell.Status.Success = false;
                Row.Status.Success = false;
            }
        }

        private bool CheckFormat()
        {
            if (Definition.Count > Row.Count)
            {
                Row.Status.Success = false;
                Row.Status.AddError(new InvalidFixtureFormatError(FixtureFormatErrorKind.NumberOfCellsInvalid));
                return false;
            }
            return true;
        }

        /// <summary>
        /// Method called before running the row
        /// </summary>
        protected virtual void BeforeRun()
        {

        }

        /// <summary>
        /// Method called after running the row
        /// </summary>
        protected virtual void AfterRun()
        {
            
        }
    }
}
